Skip to content

feat(zmq): multimodal inputs over the direct ZMQ backend (3/7) - #2056

Merged
slin1237 merged 1 commit into
mainfrom
zmq/03-multimodal
Aug 6, 2026
Merged

feat(zmq): multimodal inputs over the direct ZMQ backend (3/7)#2056
slin1237 merged 1 commit into
mainfrom
zmq/03-multimodal

Conversation

@slin1237

@slin1237 slin1237 commented Aug 5, 2026

Copy link
Copy Markdown
Member

Description

Problem

Multimodal requests (image/audio/video) cannot be served over a direct-ZMQ
engine: the adapter has no way to carry processed multimodal features and their
placeholder ranges across the vLLM EngineCore wire.

Solution

Type vLLM's multimodal feature payload (mm_features) — feature specs,
placeholder ranges, processed kwargs, and the tensor field factories
(batched / flat / shared) — and assemble the gateway's multimodal inputs
into that wire shape, including the aux tensor frames.

Changes

  • crates/engine_zmq_client/src/protocol/vllm/multimodal.rs (new) — MmFeatureSpec,
    PlaceholderRange, MmKwargsItem, and the MmField factory-tuple ser/de.
  • crates/engine_zmq_client/src/codec/tensor.rsWireTensor support used by
    multimodal payloads.
  • crates/engine_zmq_client/src/protocol/vllm/{mod,request}.rs — carry
    mm_features on the EngineCore request and emit aux tensor frames.
  • model_gateway/src/routers/grpc/{mod,zmq_client,zmq_multimodal}.rs,
    .../multimodal/assemble.rs — assemble gateway multimodal inputs into the wire
    payload (zmq_multimodal.rs new).
  • model_gateway/src/routers/grpc/backend_client.rs — return a typed build error
    on backend/variant mismatch for the ZMQ multimodal path (see note below).

Note: the multimodal path here returns a build error on backend/variant
mismatch rather than panicking (unreachable!). This is an intentional
divergence from #2041 for safety at the gateway boundary.

Test Plan

  • cargo +nightly fmt -- --check and cargo clippy --all-features clean.
  • cargo test unit coverage in multimodal.rs: field→Python factory-tuple
    encoding, factory-tuple round-trip, and feature-spec map encoding with the
    tensor ext-3 payload.
Checklist
  • cargo +nightly fmt passes
  • cargo clippy --all-targets --all-features -- -D warnings passes
  • (Optional) Documentation updated
  • (Optional) Please join us on Slack #sig-smg to discuss, review, and merge PRs

Stack (split of the oversized #2041, merge bottom-up): 01-core-dispatch →
02-structured-outputs → 03-multimodal (this PR) → 04-eos-forwarding →
05-harmony-stop-matcher → 06-e2e-infra → 07-e2e-tests. Review/merge after its
parent zmq/02-structured-outputs.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@slin1237, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 47c25536-dbc3-4837-b0a1-33598b93698c

📥 Commits

Reviewing files that changed from the base of the PR and between 1fc4f40 and 20a273d.

📒 Files selected for processing (9)
  • crates/engine_zmq_client/src/codec/tensor.rs
  • crates/engine_zmq_client/src/protocol/vllm/mod.rs
  • crates/engine_zmq_client/src/protocol/vllm/multimodal.rs
  • crates/engine_zmq_client/src/protocol/vllm/request.rs
  • model_gateway/src/routers/grpc/backend_client.rs
  • model_gateway/src/routers/grpc/mod.rs
  • model_gateway/src/routers/grpc/multimodal/assemble.rs
  • model_gateway/src/routers/grpc/zmq_client.rs
  • model_gateway/src/routers/grpc/zmq_multimodal.rs
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added multimodal input support for vLLM requests over ZMQ.
    • Supports batched, flattened, and shared multimodal tensor fields, including images and video.
    • Added automatic tensor conversion to the model’s required data type.
    • Added inline multimodal feature handling with placeholder and embedding-mask support.
  • Bug Fixes

    • ZMQ multimodal requests are no longer rejected by default.
    • Added validation and clearer errors for invalid tensor data, dimensions, metadata, and unsupported backends.
    • TokenSpeed requests now explicitly report that multimodal inputs are unsupported.

Walkthrough

This change adds typed vLLM multimodal protocol structures, direct-ZMQ tensor preprocessing, model-dtype casting, placeholder masking, and gateway integration. vLLM requests now carry assembled multimodal features, while TokenSpeed requests reject multimodal inputs.

Changes

ZMQ multimodal support

Layer / File(s) Summary
Protocol contracts and tensor casting
crates/engine_zmq_client/src/codec/tensor.rs, crates/engine_zmq_client/src/protocol/vllm/*
Adds typed multimodal wire structures, Python-compatible serialization, typed EngineCoreRequest::mm_features, and float32-to-model-dtype tensor casting.
Direct-ZMQ multimodal preprocessing
model_gateway/src/routers/grpc/zmq_multimodal.rs, model_gateway/src/routers/grpc/mod.rs
Decodes inline tensors, validates metadata, handles batched, flat, and shared fields, creates embed masks, normalizes video keys, and orders features by placeholder position.
Gateway multimodal request integration
model_gateway/src/routers/grpc/backend_client.rs, model_gateway/src/routers/grpc/multimodal/assemble.rs, model_gateway/src/routers/grpc/zmq_client.rs
Routes supported ZMQ multimodal requests through vLLM conversion, forwards typed features into EngineCore requests, and rejects TokenSpeed multimodal inputs.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GrpcRequest
  participant BackendClient
  participant ZmqMultimodal
  participant EngineCoreRequest
  GrpcRequest->>BackendClient: Provide multimodal request data
  BackendClient->>ZmqMultimodal: Convert inline tensors and metadata
  ZmqMultimodal-->>BackendClient: Return typed MmFeatures
  BackendClient->>EngineCoreRequest: Forward mm_features to vLLM
Loading

Possibly related PRs

  • smg-project/smg#570 — Introduces the vLLM multimodal structures and tensor metadata extended by this change.
  • smg-project/smg#588 — Refactors the backend-specific multimodal request path used by this integration.
  • smg-project/smg#2054 — Updates the shared ZMQ backend request construction extended here.

Suggested labels: multimodal, protocols, tests

Suggested reviewers: key4ng, catherinesue

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes adding multimodal input support over the direct ZMQ backend.
Description check ✅ Passed The description accurately explains the direct ZMQ multimodal problem, solution, scope, tests, and intentional error-handling behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch zmq/03-multimodal

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added grpc gRPC client and router changes model-gateway Model gateway crate changes labels Aug 5, 2026
Comment on lines +263 to +266
MmFieldElem {
data: Some(MmKwargValue::Tensor(decoded.whole())),
field: MmField::Shared(MmSharedField {
batch_size: num_items,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Nit: keep_on_cpu is hardcoded to false for shared fields, ignoring the on_cpu variable that the batched and flat branches use correctly. If a shared tensor's key appears in keep_on_cpu_keys, the engine will incorrectly move it to the accelerator.

Suggested change
MmFieldElem {
data: Some(MmKwargValue::Tensor(decoded.whole())),
field: MmField::Shared(MmSharedField {
batch_size: num_items,
MmFieldElem {
data: Some(MmKwargValue::Tensor(decoded.whole())),
field: MmField::Shared(MmSharedField {
batch_size: num_items,
keep_on_cpu: on_cpu,
}),

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-structured PR. The multimodal type definitions mirror the Python side faithfully, the per-item split logic (batched/flat/shared) is correct, and the dtype casting is properly handled. Good defensive error handling at multiple layers (assembly, backend dispatch, translation). One minor nit posted about keep_on_cpu in the shared-field branch.

Summary: 0 🔴 Important · 1 🟡 Nit · 0 🟣 Pre-existing

@slin1237
slin1237 force-pushed the zmq/03-multimodal branch from fc7ed51 to da17295 Compare August 5, 2026 20:20
@slin1237
slin1237 force-pushed the zmq/02-structured-outputs branch 2 times, most recently from d7077ef to a1f3dfb Compare August 5, 2026 22:30
Base automatically changed from zmq/02-structured-outputs to main August 5, 2026 23:37
@slin1237
slin1237 force-pushed the zmq/03-multimodal branch from da17295 to d8562c1 Compare August 5, 2026 23:38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
model_gateway/src/routers/grpc/zmq_client.rs (1)

194-208: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Share multimodal features across n > 1 fan-out subs.

fan_out_requests(*req) clones full vllm::GenerateRequests, including inline mm_inputs; translate_request then calls zmq_multimodal::build_mm_features separately per subsample, decoding and casting float32 tensors again and creating WireTensor bytes in every EngineCoreRequest. Build MmFeatures once before the loop and assign a refcounted slice for each sub; this removes repeated multimedia preprocessing and cuts peak gateway memory from scaling with n. Also establish a bounded inline multimodal payload limit, because the current comment explicitly validates arbitrary sizes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/zmq_client.rs` around lines 194 - 208, Update
the fan-out flow around fan_out_requests, translate_request, and
zmq_multimodal::build_mm_features to preprocess inline multimodal inputs once
before the loop, then assign each generated EngineCoreRequest a refcounted
shared MmFeatures slice. Add validation enforcing a bounded inline multimodal
payload size before preprocessing, while preserving existing request translation
and stream behavior.

Source: Coding guidelines

🧹 Nitpick comments (5)
crates/engine_zmq_client/src/protocol/vllm/multimodal.rs (1)

248-260: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🟡 Nit: Add MmField::Flat to the round-trip test.

field_round_trips_python_factory_tuple covers only Batched and Shared. Flat is the variant whose MmFieldWireInner untagged resolution depends on deny_unknown_fields distinguishing three field sets, and it carries the nested MmSlice/SliceSpec tuple encoding. That is the most fragile decode path in this module and it is only exercised on the encode side.

♻️ Add the Flat case
         for field in [
             MmField::Batched(MmBatchedField { keep_on_cpu: true }),
             MmField::Shared(MmSharedField {
                 batch_size: 4,
                 keep_on_cpu: false,
             }),
+            MmField::Flat(MmFlatField {
+                slices: vec![MmSlice::Slice(SliceSpec {
+                    start: Some(0),
+                    stop: Some(1200),
+                    step: None,
+                })],
+                dim: 0,
+                keep_on_cpu: true,
+            }),
         ] {

As per coding guidelines: "Run the pr-test-analyzer agent to verify that tests adequately cover new or changed functionality."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/engine_zmq_client/src/protocol/vllm/multimodal.rs` around lines 248 -
260, Add the MmField::Flat variant, including representative MmSlice/SliceSpec
data, to the field_round_trips_python_factory_tuple test so its nested tuple
encoding and untagged decoding are verified alongside Batched and Shared.

Source: Coding guidelines

model_gateway/src/routers/grpc/backend_client.rs (1)

368-379: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

🟡 Nit: zmq_tokenspeed_mm is unreachable today, and the into_proto(true) flag is unexplained.

Two other layers already reject TokenSpeed multimodal input: assemble_multimodal_data_impl bails for a non-vLLM ZMQ runtime, and translate_request_tokenspeed rejects any request with mm_inputs set. So this converter can only ever produce mm_inputs that the translator immediately rejects with a generic message. Add a short comment stating that the converter exists for the future TokenSpeed wire, so a reader does not assume the path works.

Also document what true selects in into_proto(true), or use a named binding. The literal carries no meaning at the call site.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/backend_client.rs` around lines 368 - 379, Add
a brief comment above zmq_tokenspeed_mm explaining that it is retained for a
future TokenSpeed wire path despite being currently unreachable. Replace the
unexplained true argument in MultimodalData::TokenSpeed conversion with a named
binding or concise explanation identifying the serialization mode it selects,
without changing behavior.
model_gateway/src/routers/grpc/zmq_multimodal.rs (1)

384-405: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🟡 Nit: Two coverage gaps, and a fixture that hides the bounds check.

  • base_inputs() uses placeholders (1, 3) and (6, 3), but several tests pass &[] as prompt_token_ids. Those ranges exceed an empty prompt. The tests pass only because is_embed_mask returns early when im_token_id is None. If you apply the bounds-check fix, give these tests a prompt of at least 9 tokens.
  • No test sets keep_on_cpu_keys, so the dropped on_cpu in the shared branch is not caught.

As per coding guidelines: "Run the pr-test-analyzer agent to verify that tests adequately cover new or changed functionality."

Also applies to: 447-469

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/zmq_multimodal.rs` around lines 384 - 405,
Update tests using base_inputs and empty prompt_token_ids to provide a prompt
with at least nine tokens, so placeholder ranges are valid once bounds checks
are enforced. Add coverage for keep_on_cpu_keys through the shared
feature-building path and assert the resulting on-CPU behavior is preserved. Run
the pr-test-analyzer agent to verify the new and changed functionality is
adequately covered.

Source: Coding guidelines

model_gateway/src/routers/grpc/multimodal/assemble.rs (1)

114-127: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

🟡 Nit: The ZMQ runtime dispatch shape differs from backend_client.rs.

This arm treats Vllm | Unspecified as vLLM and bails for anything else. BackendClient::build_chat_request and build_messages_request in model_gateway/src/routers/grpc/backend_client.rs (lines 209-236, 254-281) use the opposite shape: TokenSpeed is explicit and _ falls through to vLLM. Both functions switch on the same client.runtime(). Today ZmqEngineClient::runtime() returns only Vllm or TokenSpeed, so no live path diverges. If a third ZMQ runtime is added, assembly will reject the request while request building silently treats it as vLLM. Pick one shape for both.

Separately, assemble_vllm computes shm_enabled and shm_min_bytes, and this arm then overwrites shm_enabled and rdma_enabled. shm_min_bytes is left at its resolved value even though it no longer applies. Passing the inline-transport intent into assemble_vllm would keep the three transport fields consistent in one place.

As per coding guidelines: "Configuration changes must be checked across CLI arguments, types.rs, both conversion paths in main.rs, Python bindings, and the Go SDK."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/multimodal/assemble.rs` around lines 114 -
127, Align the ZMQ runtime dispatch in the BackendClient::Zmq arm with
BackendClient::build_chat_request and build_messages_request, keeping TokenSpeed
explicit and routing other runtimes to vLLM. Update assemble_vllm to receive the
inline-transport intent so it sets shm_enabled, rdma_enabled, and shm_min_bytes
consistently, removing the post-assembly field overrides.

Source: Coding guidelines

crates/engine_zmq_client/src/codec/tensor.rs (1)

158-159: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

🟡 Nit: Reuse the existing float32 payload instead of copying it.

from_f32_bytes_cast already owns the allocation after data.len().is_multiple_of(4) and as_chunks::<4> in crates/engine_zmq_client/src/codec/tensor.rs; the Float32 branch still calls data.to_vec(), so decode_tensor immediately discards the original Vec<u8> and allocates a second copy. Allow from_raw_bytes/from_raw to accept or take Bytes so the no-cast path can reuse the inline payload for large pixel tensors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/engine_zmq_client/src/codec/tensor.rs` around lines 158 - 159, Update
the Float32 branch in decode_tensor and the related from_raw_bytes/from_raw
constructors to accept or take ownership of Bytes, reusing the existing
from_f32_bytes_cast allocation instead of calling data.to_vec(). Preserve the
current validation and decoding behavior while eliminating the redundant payload
copy.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model_gateway/src/routers/grpc/zmq_client.rs`:
- Around line 873-885: Update the mm_inputs handling in translate_request to
validate that multimodal inputs containing tensors or hashes are not paired with
empty mm_placeholders before applying the empty-feature filter; return an error
instead of silently producing a text-only request. Preserve the existing
behavior for genuinely empty multimodal inputs, and add translation-level test
coverage in zmq_client.rs.

In `@model_gateway/src/routers/grpc/zmq_multimodal.rs`:
- Around line 257-271: Update the shared branch in the item-building logic to
set MmSharedField.keep_on_cpu from the existing on_cpu value, matching the
batched and flat branches. Preserve the shared tensor replication behavior while
ensuring keys listed in mm.keep_on_cpu_keys retain the requested CPU placement.
- Around line 217-231: Update the bounds accumulation loop in the flat-size
handling to use checked addition for total and size, returning a descriptive
error if the sum overflows instead of panicking or wrapping. Preserve the
existing negative-size validation and leading-dimension comparison after
successful accumulation.
- Around line 304-321: Update is_embed_mask to validate
offset.checked_add(length) against prompt_token_ids.len() before unwrapping or
checking im_token_id. Preserve the existing error for invalid ranges, then
return Ok(None) when im_token_id is absent only after validation succeeds.
- Around line 103-120: Update the tensor decoding flow around the float32 branch
to validate every payload length against the shape element count and the
declared dtype’s element size before returning. Continue casting float32 tensors
through WireTensor::from_f32_bytes_cast, but reject float16, bfloat16, and
float64 inputs instead of forwarding them; preserve passthrough only for
supported non-floating dtypes after validation.

---

Outside diff comments:
In `@model_gateway/src/routers/grpc/zmq_client.rs`:
- Around line 194-208: Update the fan-out flow around fan_out_requests,
translate_request, and zmq_multimodal::build_mm_features to preprocess inline
multimodal inputs once before the loop, then assign each generated
EngineCoreRequest a refcounted shared MmFeatures slice. Add validation enforcing
a bounded inline multimodal payload size before preprocessing, while preserving
existing request translation and stream behavior.

---

Nitpick comments:
In `@crates/engine_zmq_client/src/codec/tensor.rs`:
- Around line 158-159: Update the Float32 branch in decode_tensor and the
related from_raw_bytes/from_raw constructors to accept or take ownership of
Bytes, reusing the existing from_f32_bytes_cast allocation instead of calling
data.to_vec(). Preserve the current validation and decoding behavior while
eliminating the redundant payload copy.

In `@crates/engine_zmq_client/src/protocol/vllm/multimodal.rs`:
- Around line 248-260: Add the MmField::Flat variant, including representative
MmSlice/SliceSpec data, to the field_round_trips_python_factory_tuple test so
its nested tuple encoding and untagged decoding are verified alongside Batched
and Shared.

In `@model_gateway/src/routers/grpc/backend_client.rs`:
- Around line 368-379: Add a brief comment above zmq_tokenspeed_mm explaining
that it is retained for a future TokenSpeed wire path despite being currently
unreachable. Replace the unexplained true argument in MultimodalData::TokenSpeed
conversion with a named binding or concise explanation identifying the
serialization mode it selects, without changing behavior.

In `@model_gateway/src/routers/grpc/multimodal/assemble.rs`:
- Around line 114-127: Align the ZMQ runtime dispatch in the BackendClient::Zmq
arm with BackendClient::build_chat_request and build_messages_request, keeping
TokenSpeed explicit and routing other runtimes to vLLM. Update assemble_vllm to
receive the inline-transport intent so it sets shm_enabled, rdma_enabled, and
shm_min_bytes consistently, removing the post-assembly field overrides.

In `@model_gateway/src/routers/grpc/zmq_multimodal.rs`:
- Around line 384-405: Update tests using base_inputs and empty prompt_token_ids
to provide a prompt with at least nine tokens, so placeholder ranges are valid
once bounds checks are enforced. Add coverage for keep_on_cpu_keys through the
shared feature-building path and assert the resulting on-CPU behavior is
preserved. Run the pr-test-analyzer agent to verify the new and changed
functionality is adequately covered.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 88f0b447-fe17-4d59-aaaf-9d995717fd74

📥 Commits

Reviewing files that changed from the base of the PR and between 1fc4f40 and d8562c1.

📒 Files selected for processing (9)
  • crates/engine_zmq_client/src/codec/tensor.rs
  • crates/engine_zmq_client/src/protocol/vllm/mod.rs
  • crates/engine_zmq_client/src/protocol/vllm/multimodal.rs
  • crates/engine_zmq_client/src/protocol/vllm/request.rs
  • model_gateway/src/routers/grpc/backend_client.rs
  • model_gateway/src/routers/grpc/mod.rs
  • model_gateway/src/routers/grpc/multimodal/assemble.rs
  • model_gateway/src/routers/grpc/zmq_client.rs
  • model_gateway/src/routers/grpc/zmq_multimodal.rs

Comment thread model_gateway/src/routers/grpc/zmq_client.rs
Comment thread model_gateway/src/routers/grpc/zmq_multimodal.rs Outdated
Comment thread model_gateway/src/routers/grpc/zmq_multimodal.rs
Comment thread model_gateway/src/routers/grpc/zmq_multimodal.rs
Comment thread model_gateway/src/routers/grpc/zmq_multimodal.rs
@slin1237
slin1237 force-pushed the zmq/03-multimodal branch from d8562c1 to 447543f Compare August 6, 2026 00:11
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/engine_zmq_client/src/protocol/vllm/multimodal.rs (1)

247-259: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

🟡 Nit: Add round-trip coverage for MmField::Flat.

The test only round-trips Batched and Shared. Flat uses the distinct slices tuple encoding and factory conversion path. Add Flat cases for both MmSlice::Slice and MmSlice::Slices.

As per coding guidelines, “Run the pr-test-analyzer agent to verify that tests adequately cover new or changed functionality.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/engine_zmq_client/src/protocol/vllm/multimodal.rs` around lines 247 -
259, Extend field_round_trips_python_factory_tuple to include MmField::Flat
cases containing both MmSlice::Slice and MmSlice::Slices, then verify each
encodes and decodes back identically through the existing round-trip assertions.
Run the pr-test-analyzer agent to confirm coverage.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/engine_zmq_client/src/protocol/vllm/multimodal.rs`:
- Around line 247-259: Extend field_round_trips_python_factory_tuple to include
MmField::Flat cases containing both MmSlice::Slice and MmSlice::Slices, then
verify each encodes and decodes back identically through the existing round-trip
assertions. Run the pr-test-analyzer agent to confirm coverage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0513b6d9-6ae8-4201-9187-1457d0920615

📥 Commits

Reviewing files that changed from the base of the PR and between 1fc4f40 and 447543f.

📒 Files selected for processing (9)
  • crates/engine_zmq_client/src/codec/tensor.rs
  • crates/engine_zmq_client/src/protocol/vllm/mod.rs
  • crates/engine_zmq_client/src/protocol/vllm/multimodal.rs
  • crates/engine_zmq_client/src/protocol/vllm/request.rs
  • model_gateway/src/routers/grpc/backend_client.rs
  • model_gateway/src/routers/grpc/mod.rs
  • model_gateway/src/routers/grpc/multimodal/assemble.rs
  • model_gateway/src/routers/grpc/zmq_client.rs
  • model_gateway/src/routers/grpc/zmq_multimodal.rs
🚧 Files skipped from review as they are similar to previous changes (8)
  • crates/engine_zmq_client/src/codec/tensor.rs
  • model_gateway/src/routers/grpc/mod.rs
  • crates/engine_zmq_client/src/protocol/vllm/request.rs
  • crates/engine_zmq_client/src/protocol/vllm/mod.rs
  • model_gateway/src/routers/grpc/multimodal/assemble.rs
  • model_gateway/src/routers/grpc/backend_client.rs
  • model_gateway/src/routers/grpc/zmq_client.rs
  • model_gateway/src/routers/grpc/zmq_multimodal.rs

Carry preprocessed multimodal features to a vLLM EngineCore over the
direct ZMQ wire, replacing the earlier "not supported" rejection.

- Add a typed MmFeatures to the vLLM EngineCore protocol and carry it on
  EngineCoreRequest in place of the untyped placeholder; tensors ride
  inline as raw views (no /dev/shm or RDMA pull on this wire).
- Add zmq_multimodal, which performs the per-item mm-feature split the
  Python servicer would otherwise do, casting float tensors to the
  engine's model dtype (from_f32_bytes_cast).
- Assemble vLLM multimodal data for a ZMQ backend with shm/rdma disabled;
  TokenSpeed multimodal over ZMQ stays rejected (no wire slot yet).
- BackendClient converts assembled mm data to the runtime's proto,
  surfacing a backend/variant mismatch as a build error rather than a
  panic.

Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com>
@slin1237
slin1237 force-pushed the zmq/03-multimodal branch from 447543f to 20a273d Compare August 6, 2026 00:30
@slin1237
slin1237 merged commit b385926 into main Aug 6, 2026
41 of 45 checks passed
@slin1237
slin1237 deleted the zmq/03-multimodal branch August 6, 2026 02:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

grpc gRPC client and router changes model-gateway Model gateway crate changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant